package com.summary.richtext.utils;

import com.google.common.collect.Lists;
import com.summary.richtext.annotation.RichExcelField;
import com.summary.richtext.utils.richExcelUtil.Reflections;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;

public class ExportRichExcel implements Serializable {

    private final Workbook wb;

    private HSSFFormulaEvaluator hssEvaluator;

    private XSSFFormulaEvaluator xssEvaluator;

    private Sheet sheet;
    
    private String sheetTitle;

    private final String fileName;

    /**
     * 构造方法
     * @param fileName
     * @param sheetIndex
     * @param sheetTitle
     */
    public ExportRichExcel(String fileName, int sheetIndex, String sheetTitle) {
        if (fileName.toLowerCase().endsWith("xls")) {
            HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
            this.wb = hssfWorkbook;
            hssEvaluator = new HSSFFormulaEvaluator(hssfWorkbook);
        } else { //if (fileName.toLowerCase().endsWith("xlsx")) 默认以07版本导出
            XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
            this.wb = xssfWorkbook;
            xssEvaluator = new XSSFFormulaEvaluator(xssfWorkbook);
        }
        this.sheet = this.wb.createSheet();
        this.wb.setSheetName(sheetIndex,sheetTitle);
        this.sheetTitle = sheetTitle;
        this.fileName = fileName;
    }

    /**
     * 新增一个 sheet 页
     * @param sheetIndex
     * @param sheetTitle
     * @return
     */
    public ExportRichExcel increment(int sheetIndex, String sheetTitle) {
        this.sheet = this.wb.createSheet();
        this.wb.setSheetName(sheetIndex,sheetTitle);
        this.sheetTitle = sheetTitle;
        return this;
    }

    /**
     * 导入数据样式
     * @param cls
     * @param dataList
     * @throws Exception
     */
    public void setDataList(Class<?> cls, List<?> dataList) throws Exception {
        Map<String, CellStyle> styles = createStyles();
        Field[] fields = cls.getDeclaredFields();
        List<Object[]> annotationList = Lists.newArrayList();
        for (Field field : fields) {
            RichExcelField richExcelField = field.getAnnotation(RichExcelField.class);
            if (richExcelField!=null && richExcelField.type()==0 || richExcelField.type()==1){
                annotationList.add(new Object[]{richExcelField, field});
            }
        }
        annotationList.sort(Comparator.comparingInt(o -> ((RichExcelField) o[0]).sort()));
        //======================初始化大标题=====================
        Row titleRow = this.sheet.createRow(0);
        titleRow.setHeightInPoints(30.0F);
        Cell titleCell = titleRow.createCell(0);
        titleCell.setCellValue(this.sheetTitle);
        titleCell.setCellStyle(styles.get("title"));
        for (int i = 1; i < annotationList.size(); i++) {
            Cell mergedRegionCell = titleRow.createCell(i);
            mergedRegionCell.setCellStyle(styles.get("border"));
        }
        sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, annotationList.size()-1));
        //======================初始化列名=======================
        Row attrRow = this.sheet.createRow(1);//锁定行坐标
        attrRow.setHeightInPoints(20.0F);//行高
        for (Object[] item : annotationList){
            sheet.setColumnWidth(((RichExcelField) item[0]).sort()-1, 252 * ((RichExcelField)item[0]).width() + 323);
            Cell cell = attrRow.createCell(((RichExcelField)item[0]).sort()-1);
            cell.setCellStyle((CellStyle)styles.get("columnTitle"));//单元格样式
            cell.setCellValue(((RichExcelField)item[0]).title());//单元格value
        }
        if (CollectionUtils.isEmpty(dataList)){
            //无数据默认初始5行边框
            for (int i = 0; i < 5; i++) {
                Row borderRow = this.sheet.createRow(i+2);//锁定行坐标
                for (int j = 0; j < annotationList.size(); j++) {
                    Cell borderCell = borderRow.createCell(j);
                    borderCell.setCellStyle(styles.get("border"));
                }
            }
            return;
        }
        //======================导出数据集=======================
        Iterator iterator = dataList.iterator();
        Integer rowNum = 2;
        while(iterator.hasNext()) {
            Object e = iterator.next();
            Row row = sheet.createRow(rowNum++);
            Integer colunm = 0;
            Iterator var7 = annotationList.iterator();
            while(var7.hasNext()) {
                Object[] os = (Object[]) var7.next();
                RichExcelField ef = (RichExcelField) os[0];
                Object val = null;
                try {
                    if (StringUtils.isNotBlank(ef.value())) {
                        val = Reflections.invokeGetter(e, ef.value());
                    } else if (os[1] instanceof Field) {
                        val = Reflections.invokeGetter(e, ((Field) os[1]).getName());
                    }
                } catch (Exception var15) {
                    val = "";
                }

                //TODO 单元格样式整理
                Cell cell = row.createCell(colunm);
                CellStyle style = styles.get("border");
                if (ef.align()==3){
                    style = styles.get("columnTitle");
                }
                if (style == null) {
                    style = this.wb.createCellStyle();
                    style.cloneStyleFrom((CellStyle)styles.get("data" + (ef.align() >= 1 && ef.align() <= 3 ? ef.align() : "")));
                    String cellFormatString = "@";
                    style.setDataFormat(this.wb.createDataFormat().getFormat(cellFormatString));
                    styles.put("data_column_" + colunm, style);
                }
                cell.setCellStyle(style);

                try {//判断字段属性类型，根据类型置入单元格
                    if (val == null) {
                        cell.setCellValue("");
                    } else if (ef.fieldType() != Class.class) {
                        cell.setCellValue((String)ef.fieldType().getMethod("setValue", Object.class).invoke((Object)null, val));
                    } else if (val instanceof String) {
                        if (ef.rich() == true) {
                            if (hssEvaluator != null) {
                                HSSFRichTextString richTextString = getHSSFRichText(this.wb, (String) val);
                                cell.setCellValue(richTextString);
                            } else {
                                XSSFRichTextString richTextString = getXSSFRichText(this.wb, (String) val);
                                cell.setCellValue(richTextString);
                            }
                        } else {
                            cell.setCellValue((String) val);
                        }
                    } else if (val instanceof Integer) {
                        cell.setCellValue((double)(Integer)val);
                    } else if (val instanceof Long) {
                        cell.setCellValue((double)(Long)val);
                    } else if (val instanceof Double) {
                        cell.setCellValue((Double)val);
                    } else if (val instanceof Float) {
                        cell.setCellValue((double)(Float)val);
                    } else if (val instanceof Date) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        cell.setCellValue(sdf.format(val));
                    } else {
                        cell.setCellValue((String)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(), "fieldtype." + val.getClass().getSimpleName() + "Type")).getMethod("setValue", Object.class).invoke((Object)null, val));
                    }
                } catch (Exception var16) {
                    if (null != val) {
                        cell.setCellValue(val.toString());
                    }
                }
                colunm++;
            }
        }
    }

    /**
     * 07版本 实现富文本注入excel方法
     * @param wb
     * @param val
     * @return
     */
    private XSSFRichTextString getXSSFRichText(Workbook wb, String val) {

        return null;
    }

    /**
     * 03版本 实现富文本注入excel方法
     * @param wb
     * @param val
     * @return
     */
    private HSSFRichTextString getHSSFRichText(Workbook wb, String val) {

        return null;
    }

    /**
     * 默认样式设置
     * border: 单元格边框实体化
     * title: 大标题样式
     * columnTitle: 小标题样式
     * @return
     */
    private Map<String, CellStyle> createStyles() {
        Map<String, CellStyle> styles = new HashMap();
        CellStyle cellStyle;
        //==================================================================================================
        cellStyle = newCellStyle();
        cellStyle.setBorderRight(BorderStyle.THIN);//设置边框厚度
        cellStyle.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());//设置边框颜色
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        cellStyle.setWrapText(true);//显示单元格所有内容（自动转行）
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        styles.put("border", cellStyle);
        //=======================================================================================================
        cellStyle = newCellStyle();
        cellStyle.cloneStyleFrom(styles.get("border"));
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setFontHeightInPoints((short) 24);
        titleFont.setBold(true);
        cellStyle.setFont(titleFont);
        styles.put("title", cellStyle);
        //========================================================================================================
        cellStyle = newCellStyle();
        cellStyle.cloneStyleFrom(styles.get("border"));
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        Font titleFont1 = wb.createFont();
        titleFont1.setFontName("Arial");
        titleFont1.setFontHeightInPoints((short) 12);
        titleFont1.setBold(true);
        cellStyle.setFont(titleFont1);
        styles.put("columnTitle", cellStyle);
        //========================================================================================================

        return styles;
    }

    /**
     * 根据 workbook的类型 new 一个新的样式
     * @return
     */
    private CellStyle newCellStyle() {
        CellStyle cellStyle;
        if (hssEvaluator!=null) {//03版本样式
            cellStyle = ((HSSFWorkbook) this.wb).createCellStyle();
        } else {//07版本样式
            cellStyle = ((XSSFWorkbook) this.wb).createCellStyle();
        }
        return cellStyle;
    }

    /**
     * 走网址下载导出 excel 文件
     * @param response
     * @return
     * @throws IOException
     */
    public ExportRichExcel write(HttpServletResponse response) throws IOException {
        response.reset();
        response.setContentType("application/octet-stream; charset=utf-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + urlEncode(this.fileName));
        this.write(this.wb,response.getOutputStream());
        return this;
    }

    /**
     * 走输出流下载导出 excel 文件
     * @param wb
     * @param outputStream
     * @return
     * @throws IOException
     */
    private ExportRichExcel write(Workbook wb, OutputStream outputStream) throws IOException {
        wb.write(outputStream);
        return this;
    }

    /**
     * 关闭 workbook 流
     * @return
     * @throws IOException
     */
    public ExportRichExcel dispose() throws IOException {
        this.wb.close();
        return this;
    }

    /**
     * utf-8解码
     * @param part
     * @return
     */
    public static String urlEncode(String part) {
        try {
            return URLEncoder.encode(part, "UTF-8");
        } catch (UnsupportedEncodingException var2) {
            throw new RuntimeException(var2);
        }
    }
}
